This tutorial is available as a collection of HTML and graphics files and as an interactive Newton book created by Newt's Cape from these files (version and distribution information). An earlier print version appeared in PDA Developers (formerly PIE Developers), Vol. 2.4, July 1994; pp.14-18. NewtATut may also be available as an Acrobat PDF file, viewable with Adobe Acrobat Reader on your desktop system.
The "Newt" (aka NewtDevEnv) discussed in this article is a shareware programming environment on the Newton. Perhaps, you may use Newt as a generic nickname for your Newton MessagePad. So, this article will show how you can develop a simple application using Newt on your Newt -- no cables, desktop computer or computer science degree required.
You will get the most out of this interactive article/tutorial if you have Newt (application) and NewtATut (book) installed on your Newton; if you are using a web browser, you may wish to view graphics (there are some GIF screen shots). NewtATut 1.5 requires Newt 3.3 (or later).
Current Newton applications you have used or heard about, including Newt, have been constructed using the Newton ToolKit (NTK) from APDA (Apple Programmer and Developer Association). Despite being the only game in town, NTK provides an excellent framework for any large, complex, industrial-strength application. However, NTK requires a heavy investment in terms of software, hardware and training -- making it difficult and expensive for Unix-based or low-end PC/Mac developers, students or developer-wanna-bes to find out more about, and actually do, Newton development.
Like a chameleon, Newt has evolved to provide different functionality to different users. The first version of Newt in Oct. '93, inspired by the Inspector Gadget and Dot2Dot examples from Apple, allowed you to draw graphics using NewtonScript -- the "turtle", its amphibious cousin and name inspiration, used Logo. You, the learner, could explore mathematics via a turtle microworld, or add NewtonScript methods to emulate Logo commands and data structures. A separate book -- NewtTurT -- allows you to experiment interactively with the turtle.
With enthusiastic feedback from early users, I shifted efforts and emphasis
so that you could create objects based on Newton interface prototypes and
save an application. Newt's icon (next) reflects both its original turtle
personality (on left) as well as its later application personality (on
right).
If you are using the book version and have Newt installed, you can tap on its icon above to start it. If successful, Newt opens, then a few seconds later, the book reappears. You can return to Newt later by closing this book. You can return to this book from Newt by selecting its title "Building Newton Applications with Newt" from Newt's overview list (bottom dot between two scroll arrows).
Later in this book, you can tap on underlined code to copy and evaluate it in Newt. Newt also saves the source text in the Notepad (aka Notes) -- and usually exits the book when there is a visible result. If you accidentally close Newt too soon, just start over. For directions on how to create applications directly in Newt, refer to the files accompanying Newt.
For web browsers, an initial Newt screen shot appears. Use your web browser's "Back" command to return to this tutorial page. Tapping on underlined code will show you the related screen shot (if available).
Before delving into programming details, I offer a warning. Apple and Newton developers are still learning how to implement system and application software. In addition, while learning with Newt, you will take risks and make mistakes. For example, Newt allows you to execute arbitrary NewtonScript expressions. This can be a formula for enlightenment or disaster. So, remember that this is your Newton with your information and listen to your mother's advice about backing up your system.
Using common sense in following examples and suggestions, and limiting yourself to documented commands, you should reap many benefits and long hours of enjoyment from using Newt. At the same time, "a little learning [about programming] is a dangerous thing" (my apologies to Alexander Pope). I would caution against too much experimenting with random functions or methods. "Gee, I wonder what xxx does" might yield a simple error message, or it might zap a frame or soup in your system.
During the course of examples, this article will provide some glimpses, but not exhaustive explanations, of NewtonScript syntax, methods, frames, prototypes and views. Familiarity with programming concepts and syntax in general, and dynamic o-o environments like Lisp or Smalltalk in particular, would be helpful, though not necessary. For a more complete guide, I defer to the NTK documentation and introductory books on Newton programming, such as Programming for the Newton by Rhodes & McKeehan. There is a more information available in Newt's readme files, and, of course, you can register to receive a collection of program examples and a Newt manual, and to encourage me to write more interactive articles like this one.
"Hello World" is the canonical test of any programming environment. In this tutorial, you will create an application that contains a button, an about box, some input and result fields and a checkbox. You will describe these objects in NewtonScript, and add them dynamically to a live application.
Remember: to return to this book, select its title from Newt's overview list. To start with a new, empty application, tap on the following. If Newt is present, the expression should highlight before exiting to Newt to show you the result:
MyApp //:doObj('build,'MyApp) {_proto: protoApp, viewBounds: nil, /* compute in viewSetupFormScript */ title: "Hello World", viewSetupFormScript: func() begin local ap := GetAppParams(); /* use full (current) screen */ self.viewBounds := RelBounds( ap.AppAreaLeft, ap.AppAreaTop, ap.AppAreaWidth, ap.AppAreaHeight); inherited:?viewSetupFormScript(); end, ReorientToScreen: func() /* NOS 2.0 Rotate */ begin :SyncView(); :RedoChildren(); end, helpBook: :helpBookTemplate(), helpView: NIL, viewQuitScript: func() begin if helpView then helpView:close(); helpView := NIL; inherited:?viewQuitscript(); end, _package: {shortTitle: "Hello",} }
If you attempt to add an application or object that already exists, Newt replaces it. You should now have a completely functioning application complete with title and statusbar, with clock and closebox. If you are on NOS 2.0, you can Rotate this application to Landscape orientation (try this after Saving the application at the end).
To add a button to your application:
MyApp+button {_proto: protoTextButton, viewBounds: RelBounds(100,100,40,16), text: "About", buttonClickScript: func() if float exists then float:open() else PlaySound(@102), /* ROM_funbeep */ }
You define an object as a template frame, delimited by curly braces {}, with slot-value pairs separated by commas. The slot is a symbol followed by a colon. Templates typically have several slots that you supply or override:
In order to add an about box, you will add two new objects: a floating view and a text object inside that. First, add the floating view (no visible change or screen shot):
MyApp+float {_proto: protoFloatNGo, viewBounds: RelBounds(20,120,150,100), }
Now, add a text object to float. This is a special text object that can allows you to tap on embedded "HREFs", i.e., HTML URLs, such as for a mail address or web page (if urlCop or Newt's Cape is installed).
MyApp.float+aboutText {_proto: protoAboutText, text: "This demo created by <A HREF=\"mailto:" & :GetUserConfig('mailAccount) & "\">" & :GetUserConfig('name) & "</A>with the help of Newt, the lizard wizard. <A HREF=\"http://www.netaxs.com/~weyer/newton/releases.html\">Steve's releases page</A>", }
If you got an error about "NIL context", leave the (empty) About box open, and try selecting aboutText again. When you tap on the About button, the about box appears, hopefully with your name. If Newt's Cape is installed, tapping on your underlined name should post an email message to you in the Outbox; tapping on the URL will either use NIE or WebMail to retrieve the page.
You will be adding several similar input fields to the application. To do this in an object-oriented style and avoid redundant code, add a user-defined prototype to the application (this expression will highlight, but not exit to Newt since no visible change occurs) (no screenshot):
MyApp.myInputProto {_proto: protoInputLine, text: "", value: 0, viewFlags: 10753, /* vVisible+vClickable+vGesturesAllowed+vNumbersAllowed */ getTextValue: func() /* from text, set number value (used by total) */ begin self.value := StringToNumber(text); if not value then value := 0; end, viewChangedScript: func(slot,view) if slot='text then begin :getTextValue(); if total exists then total:update(); end, viewSetupFormScript: func() begin self.text := clone(text); :getTextValue(); inherited:?viewSetupFormScript(); end, }
Before creating some instances, first, some background on user prototypes. This user prototype is based on/inherits from a built-in prototype -- protoInputLine. The viewFlags slot specifies recognition behavior -- you override viewFlags to assure that your input field will recognize mainly numbers. The value of viewFlags is based on a set of bit switches. These switch names (indicated in the comment) are available in NTK at compile-time, and can be made available in Newt during development by linking constants via plug-in modules. It is easiest, for now, to provide the "magic numbers" yourself -- a little crude but workable.
Next, if you want to provide a way of describing what should happen after text is entered into a field, you add a viewChangedScript method. Here, it converts the string value of its text slot to a real number, caches it in a value slot you added, and sets value to 0 if it is nil (StringToNumber returns this for empty strings). (It would have been less cumbersome to write value := StringToNumber(text) or 0; but NewtonScript's boolean operators, unfortunately, return only true or nil, although they will operate on any kind of value.) It then asks total, if it exists, to update. There are two new pieces of NewtonScript syntax: first, you can include multiple statements in a begin...end statement, separated by semi-colons; second, you can branch on logical tests using the if...then...else conditional statement (the else clause is optional).
Finally, to ensure that the field is correctly initialized, you specialize the viewSetupFormScript method. This makes a copy (clone) of the prototype's text string and sets an initial numerical value in the view. viewSetupFormScript conditionally sends the same message to the system variable inherited -- always a good idea when you override a system method -- so that protoInputLine can perform any additional initialization.
Now, add two input fields that use this user prototype:
MyApp+num1 {_proto: myInputProto, viewBounds: RelBounds(130,20,100,20), }
You can write numbers into the input field, use the scrub gesture to erase, and double tap to popup a numeric keypad. This inherits slots, including behavior, from myInputProto, the user prototype you defined earlier. Now, add the second field:
MyApp+num2 {_proto: myInputProto, viewBounds: RelBounds(130,45,100,20), }
You might have noticed that viewChangedScript in myInputProto attempts to update the total field, but only if total exists. You will now create a total field:
MyApp+total {_proto: protoStaticText, viewBounds: RelBounds(130,80,100,16), text: "Total", /* initial text */ numVars: ['num1, 'num2], getValueText: func() /* return text from summing field values */ begin local tot := 0, field; foreach field in numVars /* add up num1.value + num2.value etc. */ do tot := tot + GetVariable(self,field).value; if round exists and round.viewValue then tot := RIntToL(tot); NumberStr(tot); /* return string */ end, update: func() SetValue(self,'text,:getValueText()), viewSetupFormScript: func() begin self.text := :getValueText(); inherited:?viewSetupFormScript(); end, }
When you change any of your input fields, the total should update automatically. Notice that the numVars slot is initialized to contain an array of symbols naming the fields to be totalled, in this case, ['num1, 'num2,]. In the update method, you declare several local variables, and iterate over this array using the handy NewtonScript foreach construct. Since the field is a symbol name, GetVariable looks up the field name, such as num1, in the current context to obtain a reference to an input field frame. By using inheritance, GetVariable finds the name defined in total's parent, in this case myApp, where num1 and num2 are defined. Next, it may round the value, using the non-mnemonically named built-in function RIntToL, depending on the state of a yet-to-be-added checkbox named round. Finally, it converts the number tot to a string using NumberStr, and sets its text field. Using the SetValue function ensures not only that the text slot is set to the new string, but also that the Newton view system will update the screen to reflect this change.
Finally, since you provided a little code in update to handle rounding of the result, you can now add a checkbox object named round as follows:
MyApp+round {_proto: protoCheckbox, viewBounds: RelBounds(20,78,50,16), text: "Round?", valueChanged: func() if total exists then total:update(), }
Now, when you enter numbers with decimal points, most easily via the keypad, you can affect whether the total is shown as a decimal number or a rounded integer by toggling the checkbox.
Although you already have an About button, a more standard way to access About information (as well as Help and Prefs) is to include an info button. Newt supplies protoInfoButton for 1.x; this is standard for NOS 2.0. Normally, this button would be placed automatically within the status bar -- here we just position it near the bottom of the app (you may need to move Eval Controls by dragging its bottom edge). If you select Help, the book is initially empty.
MyApp+info {_proto: protoInfoButton, viewJustify: 134, /* vjCenterH + vjCenterV + vjParentBottomV */ viewBounds: RelBounds(25,-18,13,13), DoInfoAbout: func() if float exists then float:open() else PlaySound(@102), /* ROM_funbeep */ DoInfoHelp: func() begin local ttim := GetRoot().tinyTim; if not helpView then helpView := BuildContext({ _proto: ttim._proto, bookRef: helpBook}); ttim:close(); helpView:openManual(helpBook); end, }
You can create a simple help book directly in Newt (you can create more elaborate help books from HTML using Newt's Cape -- helpurl example). You included helpBook and helpView slots when defining the application initially. Now, you will add several trivial pages to the help book.
MyApp.helpBook+page1 .subject 1 Describe Hello World .story Read the NewtATut tutorial.
MyApp.helpBook+page2 .subject 1 Use Hello World .story Very simple -- just write in some numbers.
When finished, tap the Info button and select Help.
Of course, your application is already running within Newt. Also, you can reconstruct it quickly in a later session with Newt from your method sources saved in the NotePad -- you can build from existing sources by Evaluating :doObj('build,'myApp) or using the Build(App) command with one of the sources selected. Perhaps you might like to edit the methods to change the name of the button, for example. Newt automatically compiles methods in the curent folder. You then would need to re-create the application and objects.
However, if you would like to save your finished application in a form so that you do not need Newt or so that you can give your application to someone without distributing your source code, you can save it as a package using the NewtPack plug-in.
To save your application from Newt, the NewtPack package must be installed (a version comes with Newt).
Eval this expression:
:saveApp(MyApp)
Or, if your app is open, you can tap Newt's Save button.
If NewtPack was installed and the save was successful, you can exit Newt and tap on the Hello icon in Extras (old screenshot). On NOS 2.0, be sure to test with Rotate.
I hope that this whirlwind tour has provided a general introduction of how you can write NewtonScript, create objects and save this as an application on your very own Newton. Although this is a simple example, you can take the same basic ingredients, plus a few more, and concoct more interesting and complex recipes using Newt.
Answers to the following (and other) questions may be found in the Newt FAQ:
NewtATut (in all its formats) is © 1994-97. Steve Weyer. All Rights Reserved Worldwide. NewtATut is freeware. If all of its files are included, NewtATut may be freely distributed on online services. Book version created from HTML by Newt's Cape.
Version: 1.5. Last updated: 26 Jun 97
Over the past 20+ years, Steve has managed and implemented R&D projects in object-oriented languages and prototyping environments, AI tools, hypertext systems and education and pharmaceutical applications. A Silicon Valley emigre and Seattle native, Steve lives with his family in rural Pennyslvania, develops Newton apps in his spare time, and currently works for Gaia Software on handheld wireless access to intranets.